perm filename EFTP.BPL[11,HE] blob sn#656302 filedate 1982-04-29 generic text, type T, neo UTF8
// EFTP.BPL
// Copyright Xerox Corporation 1979

GET "EFTP.HDR"

//----------------------------------------------------------------
LET INITEFTPPACKAGE(PACKAGEZONE) BE [ LET A=0 ]
//----------------------------------------------------------------


//----------------------------------------------------------------
AND OPENEFTPSOC(SOC,LCLPORT,FRNPORT) BE
//----------------------------------------------------------------
[
ZERO(SOC,LENEFTPSOC)
SOC!TRANSFERNOTSTARTED:= TRUE
SOC!CURRENTTIMEOUT:= STARTINGTIMEOUT
OPENLEVEL1SOCKET(SOC,LCLPORT,FRNPORT)
]


//----------------------------------------------------------------
AND CLOSEEFTPSOC(SOC) BE
//----------------------------------------------------------------
[
IF SOC!ABORTPBI NE 0 THEN
   RELEASEPBI(SOC!ABORTPBI)
CLOSELEVEL1SOCKET(SOC)
]


//----------------------------------------------------------------
AND SENDEFTPBLOCK(SOC,ADDR,BYTECNT,TIMEOUT) = VALOF
//----------------------------------------------------------------
[
LET RESULT = SENDEFTPPACKET(SOC,ADDR,BYTECNT,TIMEOUT,TYPEEFTPDATA)
IF RESULT GE 0 THEN SOC!SEQNUM:= SOC!SEQNUM+1
RESULTIS RESULT
]


//----------------------------------------------------------------
AND SENDEFTPEND(SOC,TIMEOUT) = VALOF
//----------------------------------------------------------------
//THE END SEQUENCE REQUIRES A COMPLETE ACK FOR THE FIRST
//OUTGOING END, AND THEN AN EXTRA END IS SENT, AS PART OF THE
//THREE WAY HANDSHAKE.
[
IF SENDEFTPPACKET(SOC,0,0,TIMEOUT,TYPEEFTPEND) GE 0 THEN
   [ //FIRST END SENT AND ACKED OK
   LET PBI = NIL
   SOC!SEQNUM:= SOC!SEQNUM+1
   //SECOND END NEEDS NO TIMEOUT OR ACK
   PBI:= GETPBI(SOC,FALSE)
   PBI!ID2:= SOC!SEQNUM
   COMPLETEPUP(PBI,TYPEEFTPEND,PUPOVBYTES)
   RESULTIS TRUE
   ]
RESULTIS FALSE
]


//----------------------------------------------------------------
AND SENDEFTPPACKET(SOC,ADDR,DATABYTES,TIMEOUT,TYP) = VALOF
//----------------------------------------------------------------
// THIS IS A GENERAL ROUTINE, USABLE FOR DATA OR END PACKETS
// WILL HANDLE RETRANSMISSIONS HERE. PASSES IN A LONG TIMEOUT
// RETURNS THE NUMBER OF DATA BYTES SENT (>=0) IF OK;
// RETURNS AN ERROR CODE (< 0) IF THERE WAS AN ERROR
[
LET LONGTIMER = NIL; 
SETTIMER(LV SOC!STARTTIME,0)  //REMEMBER START TIME
SETTIMER(LV LONGTIMER,TIMEOUT)

   [
   // CONSTRUCT THE OUTGOING PACKET
   LET SHORTTIMER = NIL
   LET PBI = GETPBI(SOC,FALSE)
   PBI!ID2:= SOC!SEQNUM
   IF DATABYTES GR 0 THEN 
      MOVEBLOCK(LV PBI!(WORDS+0),ADDR,(DATABYTES+1)/2)
   COMPLETEPUP(PBI,TYP,DATABYTES+PUPOVBYTES)
   SETTIMER(LV SHORTTIMER,SOC!CURRENTTIMEOUT)

      [
      BLOCK() REPEATUNTIL SOC!IQHEAD NE 0 |
       (TIMEOUT NE -1 & TIMERHASEXPIRED(LV LONGTIMER)) |
       TIMERHASEXPIRED(LV SHORTTIMER)
      IF TIMEOUT NE -1 & TIMERHASEXPIRED(LV LONGTIMER) THEN
         RESULTIS EFTPTIMEOUT   //HE IS NOT ANSWERING NOW
      IF TIMERHASEXPIRED(LV SHORTTIMER) THEN
         [
         //EXPONENTIALLY AGE RETRANSMISSION INTERVAL
         AGETIMEOUT(SOC)
         BREAK    //RE-TRANSMIT
         ]
      PBI:= DEQUEUE(LV SOC!IQ); IF PBI EQ 0 BREAK

      //CHECK THE SOURCE OF THIS PACKET
      UNLESS MULTEQ(LV SOC!FRNSOCKET,LV PBI!SSOCKET,2) DO
         [ RELEASEPBI(PBI); LOOP ]

      //GOT A PACKET REALLY FOR US FROM OUR PARTNER
      AGETIMEOUT(SOC)
      SWITCHON (PBI!TYPE & #377) INTO
         [
         CASE TYPEEFTPACK:
            [
            IF PBI!ID2 EQ SOC!SEQNUM THEN
               [ RELEASEPBI(PBI); RESULTIS DATABYTES ]
            IF PBI!ID2 GR SOC!SEQNUM THEN
               [
               SENDEFTPABORT(SOC,OUTOFSYNCHABORT,
                "YOUR RECEIVER HAS GOTTEN OUT OF SYNCH.",PBI)
               RELEASEPBI(PBI)
               RESULTIS EFTPABORTSENT
               ]
            RELEASEPBI(PBI)
            ENDCASE
            ]
         CASE TYPEEFTPABORT:
            [
            IF SOC!ABORTPBI NE 0 THEN
               RELEASEPBI(SOC!ABORTPBI)
            SOC!ABORTPBI:= PBI
            RESULTIS EFTPABORTRECEIVED // BAIL OUT
            ]
         DEFAULT: RELEASEPBI(PBI)
         ]   
      ] REPEAT
   ] REPEAT
]


//----------------------------------------------------------------
AND AGETIMEOUT(SOC) BE
//----------------------------------------------------------------
//UPDATE THE ADAPTIVE TIMEOUT
// = 2 TIMES THE AVERAGE RESPONSE TIME, EXPONENTIALLY AGED OVER
// THE LAST 8 SAMPLES.
[
LET T = NIL; SETTIMER(LV T,0)
T:= ((T-SOC!STARTTIME) LSHIFT 3) +4
SOC!CURRENTTIMEOUT:= (7*SOC!CURRENTTIMEOUT+
 MAX(MINTIMEOUT,MIN(MAXTIMEOUT,T))) RSHIFT 3
]


//----------------------------------------------------------------
AND RECEIVEEFTPBLOCK(SOC,ADDR,TIMEOUT) = VALOF
//----------------------------------------------------------------
// RETURNS A BYTE COUNT (>=0) OR AN ERROR CODE (<0)
    [
    LET INPBI = 0
    LET RECEIVERESULT = RECEIVEEFTPPACKET(SOC, TIMEOUT, LV INPBI)
    IF RECEIVERESULT GR 0 THEN // SUCCEEDED
    	[
	MOVEBLOCK(ADDR, LV INPBI!(WORDS+0),
		(RECEIVERESULT+1) RSHIFT 1)
	IF INPBI THEN [ RELEASEPBI(INPBI); IF RECEIVERESULT EQ 0 LOOP ] // IGNORE ZERO-LENGTH PACKETS
	RESULTIS RECEIVERESULT // COUNT OF DATABYTES
	]
    UNLESS RECEIVERESULT EQ EFTPNOTFIRSTSYNCH RESULTIS RECEIVERESULT
    ] REPEAT

//------------------------------------------------------------------
AND RECEIVEEFTPPACKET(SOC, TIMEOUT, LVPBI) = VALOF
//------------------------------------------------------------------
// RETURNS A BYTE COUNT (>=0) OR AN ERROR CODE (<0)
// IF A BYTE COUNT, @LVPBI CONTAINS THE PBI WITH THE DATA
// NOTE THAT WHEN WE START, WE MAY NOT YET KNOW THE SOCKET
//  NUMBER OF THE FOREIGN SOCKET.....
   [

LET LONGTIMER = NIL; SETTIMER(LV LONGTIMER,TIMEOUT)
   [
   LET INPBI = NIL
   LET INSEQNUM = NIL
   // WAIT FOR AN INCOMING PACKET

   BLOCK() REPEATUNTIL (SOC!IQHEAD NE 0) |
      (TIMEOUT NE -1 & TIMERHASEXPIRED(LV LONGTIMER))

   IF TIMEOUT NE -1 & TIMERHASEXPIRED(LV LONGTIMER) THEN
      RESULTIS EFTPTIMEOUT
   INPBI:= DEQUEUE(LV SOC!IQ)
   IF INPBI EQ 0 THEN CALLSWAT("[EFTP] - IQ EMPTY.")
   INSEQNUM:= INPBI!ID2

   // SEE IF WE ARE ALREADY COMMITTED TO A FILE TRANSFER
   IF SOC!TRANSFERNOTSTARTED THEN
      [
      // NO ESTABLISHED FILE TRANSFER IS IN PROGRESS.
      // TO START A FILE TRANSFER, HIS MUST BE A VALID PACKET 0,
      //  AND ITS SOURCE MUST MATCH OUR SOCKET'S
      //  DESTINATION, OR WE MUST BE LISTENING.
      LET ISFORUS = (((SOC!FRNHOST & #377) EQ 0) |  // [ WE ARE LISTENING ] OR
            (SOC!FRNPORT EQ INPBI!SPORT))  &        // [ HIS SOURCE IS US ]
            ((INPBI!TYPE & #377) EQ TYPEEFTPDATA)   // AND IT'S A VALID PACKET
      TEST ISFORUS & INSEQNUM EQ 0
	THEN // ESTABLISH A "CONNECTION"
            [
            SOC!TRANSFERNOTSTARTED:= FALSE
            SOC!SEQNUM:= 0
            MOVEBLOCK(LV SOC!FRNPORT,
             LV INPBI!SPORT,3)
            ]
	OR // RESTART REQUEST OR RANDOM PACKET FROM ANOTHER HOST
	    [
	    IF ISFORUS THEN SENDEFTPABORT(SOC, OUTOFSYNCHABORT,
		"YOUR SENDER HAS GOTTEN (SIC) OUT OF SYNCH", INPBI)
	    RELEASEPBI(INPBI)
	    TEST ISFORUS THEN RESULTIS EFTPNOTFIRSTSYNCH OR LOOP
	    ]
       ]

   //IF YOU REACH HERE, THE TRANSFER HAS ALREADY COMMENCED
   UNLESS MULTEQ(LV SOC!FRNSOCKET,LV INPBI!SSOCKET,2) DO
      [
      IF (INPBI!TYPE & #377) EQ TYPEEFTPDATA & INSEQNUM EQ 0 THEN
         [
         SENDEFTPABORT(SOC,RECEIVERBUSYABORT,
          "RECEIVER BUSY, PLEASE TRY AGAIN LATER.",INPBI)
         SOC!SOMEONEELSEWAITING:= TRUE
         ]
      RELEASEPBI(INPBI)
      LOOP   //WAIT FOR NEXT INPUT
      ]

   //IF WE RECEIVE A DATA PACKET WITH SEQUENCE NUMBER 0
   //FROM OUR PARTNER, THEN ASSUME HE WANTS TO RESTART.
   IF INSEQNUM EQ 0 & SOC!SEQNUM GR 1 THEN  
      [ RELEASEPBI(INPBI); RESULTIS EFTPRESETRECEIVED ]

   //CHECK THE SEQUENCE NUMBER -- SHOULD BE EXPECTED OR EXPECTED-1
   IF (SOC!SEQNUM-INSEQNUM) RSHIFT 1 NE 0 THEN 
      [
      SENDEFTPABORT(SOC,OUTOFSYNCHABORT,
         "YOUR SENDER HAS GOTTEN OUT OF SYNCH",INPBI)
      RELEASEPBI(INPBI)
      RESULTIS EFTPABORTSENT
      ]

   SWITCHON (INPBI!TYPE & #377) INTO
      [
      CASE TYPEEFTPDATA:
         [
         LET DATABYTES = NIL
         ACKEFTPPACKET(SOC,INPBI)
         IF INSEQNUM NE SOC!SEQNUM THEN
            [ RELEASEPBI(INPBI); LOOP ]  //RETRANSMISSION
         SOC!SEQNUM:= SOC!SEQNUM+1
         DATABYTES:= INPBI!LENGTH - PUPOVBYTES
         RV LVPBI:= INPBI
         RESULTIS DATABYTES   //AND RETURN
         ]
      CASE TYPEEFTPEND:
         [
         LET DALLYTIMER = NIL 
         ACKEFTPPACKET(SOC,INPBI); RELEASEPBI(INPBI)
         IF INSEQNUM NE SOC!SEQNUM LOOP  //RETRANSMISSION
         SOC!SEQNUM:= SOC!SEQNUM+1
         SETTIMER(LV DALLYTIMER,DALLYTIMEOUT)
            [
            BLOCK() REPEATUNTIL SOC!IQHEAD NE 0 |
             TIMERHASEXPIRED(LV DALLYTIMER)

            IF TIMERHASEXPIRED(LV DALLYTIMER) THEN 
               RESULTIS EFTPENDRECEIVED
            INPBI:= DEQUEUE(LV SOC!IQ)
            IF INPBI EQ 0 THEN CALLSWAT("[EFTP] - IQ EMPTY.")

            IF (INPBI!TYPE & #377) NE TYPEEFTPEND THEN
               [ RELEASEPBI(INPBI); LOOP ]
            IF SOC!SEQNUM EQ INPBI!ID2 THEN   
               [ RELEASEPBI(INPBI); RESULTIS EFTPENDRECEIVED ]

            // WAS A RETRANSMISSION OF FIRST END, ACK IT
            ACKEFTPPACKET(SOC,INPBI); RELEASEPBI(INPBI)
            ]  REPEAT
         ENDCASE
         ]
      CASE TYPEEFTPABORT:
         [
         IF SOC!ABORTPBI NE 0 THEN
            RELEASEPBI(SOC!ABORTPBI)
         SOC!ABORTPBI:= INPBI
         RESULTIS EFTPABORTRECEIVED
         ]
      ]
   RELEASEPBI(INPBI)
   ] REPEAT
]
   

//----------------------------------------------------------------
AND ACKEFTPPACKET(SOC,PBI) BE
//----------------------------------------------------------------
[
// CONSTRUCT THE OUTGOING ACK PACKET
LET ACKPBI = GETPBI(SOC,FALSE)
ACKPBI!ID2:= PBI!ID2
COMPLETEPUP(ACKPBI,TYPEEFTPACK,0+PUPOVBYTES)
]


//----------------------------------------------------------------
AND GETEFTPABORT(SOC) = SOC!ABORTPBI
//----------------------------------------------------------------


//----------------------------------------------------------------
AND SENDEFTPABORT(SOC,ABORTCODE,ABORTSTRING,PBI) BE
//----------------------------------------------------------------
// CONSTRUCT THE OUTGOING ABORT PACKET,
// WILL GET SENT TO YOUR CURRENT FOREIGN PORT
[
LET ABORTPBI = GETPBI(SOC,FALSE)
IF PBI NE 0 THEN
   [
   ABORTPBI!ID2:= PBI!ID2
   SETPUPDPORT(ABORTPBI,LV PBI!SPORT)
   ]
ABORTPBI!ID2:= SOC!SEQNUM
ABORTPBI!(WORDS+0):= ABORTCODE
APPENDSTRINGTOPUP(ABORTPBI,3,ABORTSTRING)
COMPLETEPUP(ABORTPBI,TYPEEFTPABORT,0)
]